// Copyright (C) 2009 Jesse Jones
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using MmmDoc;
using Mono.Cecil;
using System;
using System.Linq;

namespace MmmDoc.Database
{
	/// <summary>Base class for <see cref = "Database.TypeDef"/>.</summary>
	/// <remarks>Also used to reference types not being documented or imported types.</remarks>
	internal class TypeName : CodeElement
	{
		/// <summary>Returns a TypeDef where possible.</summary>
		public static TypeName Create(Document doc, TypeReference type)
		{
			if (type is GenericInstanceType)
				return new TypeGeneric(doc, (GenericInstanceType) type);
			else if (type is TypeSpecification)
				return new TypeSpec(doc, (TypeSpecification) type);
				
			TypeDef td = null;
			for (int i = 0; i < doc.Types.Length && td == null; ++i)	// not sure why SingleOrDefault gives us an "invalid operation" exception...
			{
				if (doc.Types[i].FullName == type.FullName)
					td = doc.Types[i];
			}
			
			if (td != null)
				return td;
			else
				return new TypeName(doc, type);
		}
		
		protected TypeName(Document doc, TypeReference type) : base(type.FullName)
		{
			if (type.DeclaringType != null)
				Name = type.DeclaringType.Name + '/' + type.Name;
			else
				Name = type.Name;
			Name = Name.StripBackTicks();
			
			TypeReference t = type;
			while (t.DeclaringType != null)
				t = t.DeclaringType;
			Namespace = t.Namespace;
			
			m_doc = doc;
			m_type = type;
		}
		
		/// <summary>Fully qualified namespace, e.g. "System.IO".</summary>
		/// <remarks>Will be empty if the type is in the global namespace.</remarks>
		public string Namespace {get; private set;}
		
		/// <summary>(Mostly) unadorned type name.</summary>
		/// <remarks>May include generic type information.</remarks>
		public string Name {get; private set;}
		
		internal virtual string GetXmlName()
		{
			var builder = new System.Text.StringBuilder();
			
			if (m_type.DeclaringType != null)
			{
				TypeName parent = TypeName.Create(m_doc, m_type.DeclaringType);
				builder.Append(parent.GetXmlName());
				builder.Append('.');
				
				builder.Append(m_type.Name);
			}
			else
			{
				if (Namespace.Length > 0)
				{
					builder.Append(Namespace);
					builder.Append('.');
				}
				
				builder.Append(Name);
			}
			
			if (m_type.HasGenericParameters)
			{
				builder.Append('`');
				builder.Append(m_type.GenericParameters.Count.ToString());
			}
			
			return builder.ToString();
		}
		
		#region Fields
		private Document m_doc;
		private TypeReference m_type;
		#endregion
	}
}
